package suncertify.service.remote;

import suncertify.util.Messages;
import suncertify.util.Options;
import suncertify.util.Notification;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.RemoteException;
import java.rmi.NotBoundException;
import java.rmi.ConnectIOException;
import java.rmi.ConnectException;
import java.rmi.server.UnicastRemoteObject;
import java.io.File;
import java.io.IOException;
import java.util.Observable;

/**
 * This class implements RMI server functionality
 */
public class Server extends Observable {
    private static final Messages msg = Messages.getInstance();
    private static final Options opts = Options.getInstance();
    private static final String JAR_NAME = "runme.jar";
    private static final String RMI_PATH = "/bin/rmiregistry";
    private static final Server instance = new Server();
    
    private Process rmiProc = null;
    private Registry r;
    private boolean started = false;


    private Server() {
        File file = new File(JAR_NAME);
        System.setProperty("java.rmi.server.codebase", file.toURI().toString());
    }

    /**
     * returns the instance of Server
     * @return server instance
     */
    public static Server getIntance() {
        return instance;
    }

    /**
     * starts the server, creates and binds all remote objects
     */
    public synchronized void start() {
        try {
            sendNotification(ServerAction.STARTING);
            r = LocateRegistry.getRegistry();
            RemoteDataImpl impl = new RemoteDataImpl(opts.get(Options.REMOTE_DB_FILE));
            RemoteData data = (RemoteData) UnicastRemoteObject.exportObject(impl, 0);
            r.rebind(RemoteData.REMOTE_NAME, data);
            started = true;
            sendNotification(ServerAction.STARTED);
        } catch (ConnectIOException e) {
            sendNotification(ServerAction.RMI_CONNECT_ERROR);
        } catch (ConnectException e) {
            sendNotification(ServerAction.RMI_CONNECT_ERROR);
        } catch (RemoteException e) {
            sendNotification(ServerAction.ERROR, msg.get("server.remote.error.rmi"));
        } catch (Exception e) {
            sendNotification(ServerAction.ERROR, e.getMessage());
        }
    }

    /**
     * stop the service and unbinds all remote objects
     */
    public synchronized void stop() {
        if (r == null) {
            sendNotification(ServerAction.ERROR, msg.get("server.remote.error.not.started"));
            return;
        }
        started = false;
        try {
            sendNotification(ServerAction.STOPING);
            r.unbind(RemoteData.REMOTE_NAME);
            r = null;
        } catch (RemoteException e) {
            sendNotification(ServerAction.ERROR, msg.get("server.remote.error.rmi"));
        } catch (NotBoundException e) {
            //do nothing
        }
        sendNotification(ServerAction.STOPED);
    }

    private void sendNotification(ServerAction action, String message) {
        setChanged();
        notifyObservers(new Notification(action, message));
    }

    private void sendNotification(ServerAction action) {
        setChanged();
        notifyObservers(new Notification(action));
    }

    /**
     * test started server or not
     * @return true, if started, else - false
     */
    public boolean isStarted() {
        return started;
    }

    /**
     * trying to start rmiregistry application
     */
    public synchronized void startRMI() {
        String javaHome = System.getProperty("java.home");
        String rmiRegPath = javaHome.concat(RMI_PATH);
        try {
            rmiProc = Runtime.getRuntime().exec(rmiRegPath);
            sendNotification(ServerAction.RMI_STARTED);
        } catch (IOException e) {
            sendNotification(ServerAction.ERROR, msg.get("rmi.start.failed", rmiRegPath));
        }
    }

    /**
     * shutdowns the server, unbinds remote objects and
     * destroy rmiregistry process, if they has been launched
     */
    public synchronized void shutdown() {
        if (started) {
            stop();
        }
        if (rmiProc != null) {
            rmiProc.destroy();
            rmiProc = null;
        }
    }
}
